home *** CD-ROM | disk | FTP | other *** search
/ CICA 1993 April / CICA MS Windows - April 1993.iso / unzipped / util / mews11 / line.c < prev    next >
C/C++ Source or Header  |  1992-03-12  |  29KB  |  1,188 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include    <stdio.h>
  15. #include    "estruct.h"
  16. #include    "eproto.h"
  17. #include    "edef.h"
  18. #include    "elang.h"
  19.  
  20. #define    BSIZE(a)    (a + NBLOCK - 1) & (~(NBLOCK - 1))
  21.  
  22. static long last_size = -1L;    /* last # of bytes yanked */
  23.  
  24. /*
  25.  * This routine allocates a block of memory large enough to hold a LINE
  26.  * containing "used" characters. Return a pointer to the new block, or
  27.  * NULL if there isn't any memory left. Print a message in the message
  28.  * line if no space.
  29.  */
  30.  
  31. LINE *PASCAL NEAR lalloc(used)
  32.  
  33. register int used;
  34.  
  35. {
  36.     register LINE    *lp;
  37.  
  38.     if ((lp = (LINE *)malloc(sizeof(LINE)+used)) == NULL) {
  39.         mlabort(TEXT94);
  40. /*                      "%%Out of memory" */
  41.         return(NULL);
  42.     }
  43.     lp->l_size = used;
  44.     lp->l_used = used;
  45. #if     WINDOW_MSWIN
  46.         {
  47.             static int  o = 0;
  48.             if (--o < 0) {
  49.                 longop(TRUE);
  50.                 o = 10;     /* to lower overhead, only 10% calls to longop */
  51.             }
  52.         }
  53. #endif
  54.     return(lp);
  55. }
  56.  
  57. /*
  58.  * Delete line "lp". Fix all of the links that might point at it (they are
  59.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  60.  * might be in. Release the memory. The buffers are updated too; the magic
  61.  * conditions described in the above comments don't hold here.
  62.  */
  63. PASCAL NEAR lfree(lp)
  64. register LINE    *lp;
  65. {
  66.     register BUFFER *bp;
  67.     SCREEN *scrp;        /* screen to fix pointers in */
  68.     register WINDOW *wp;
  69.     register int cmark;        /* current mark */
  70.  
  71.     /* in all screens.... */
  72.     scrp = first_screen;
  73.     while (scrp) {
  74.         wp = scrp->s_first_window;
  75.         while (wp != NULL) {
  76.             if (wp->w_linep == lp)
  77.                 wp->w_linep = lp->l_fp;
  78.             if (wp->w_dotp    == lp) {
  79.                 wp->w_dotp  = lp->l_fp;
  80.                 wp->w_doto  = 0;
  81.             }
  82.             for (cmark = 0; cmark < NMARKS; cmark++) {
  83.                 if (wp->w_markp[cmark] == lp) {
  84.                     wp->w_markp[cmark] = lp->l_fp;
  85.                     wp->w_marko[cmark] = 0;
  86.                 }
  87.             }
  88.             wp = wp->w_wndp;
  89.         }
  90.  
  91.         /* next screen! */
  92.         scrp = scrp->s_next_screen;
  93.     }
  94.  
  95.     bp = bheadp;
  96.     while (bp != NULL) {
  97.         if (bp->b_nwnd == 0) {
  98.             if (bp->b_dotp    == lp) {
  99.                 bp->b_dotp = lp->l_fp;
  100.                 bp->b_doto = 0;
  101.             }
  102.             for (cmark = 0; cmark < NMARKS; cmark++) {
  103.                 if (bp->b_markp[cmark] == lp) {
  104.                     bp->b_markp[cmark] = lp->l_fp;
  105.                     bp->b_marko[cmark] = 0;
  106.                 }
  107.             }
  108.         }
  109.         bp = bp->b_bufp;
  110.     }
  111.     lp->l_bp->l_fp = lp->l_fp;
  112.     lp->l_fp->l_bp = lp->l_bp;
  113.     free((char *) lp);
  114. #if     WINDOW_MSWIN
  115.         {
  116.             static int  o = 0;
  117.             if (--o < 0) {
  118.                 longop(TRUE);
  119.                 o = 10;     /* to lower overhead, only 10% calls to longop */
  120.             }
  121.         }
  122. #endif
  123. }
  124.  
  125. /*
  126.  * This routine gets called when a character is changed in place in the current
  127.  * buffer. It updates all of the required flags in the buffer and window
  128.  * system. The flag used is passed as an argument; if the buffer is being
  129.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  130.  * mode line needs to be updated (the "*" has to be set).
  131.  */
  132. PASCAL NEAR lchange(flag)
  133. register int    flag;
  134. {
  135.     register WINDOW *wp;
  136.     SCREEN *scrp;        /* screen to fix pointers in */
  137.  
  138.     if (curbp->b_nwnd != 1)         /* Ensure hard.     */
  139.         flag = WFHARD;
  140.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so    */
  141.         flag |= WFMODE;         /* update mode lines.    */
  142.         curbp->b_flag |= BFCHG;
  143.     }
  144.  
  145.     /* in all screens.... */
  146.     scrp = first_screen;
  147.     while (scrp) {
  148.         /* make sure all the needed windows get this flag */
  149.         wp = scrp->s_first_window;
  150.         while (wp != NULL) {
  151.             if (wp->w_bufp == curbp)
  152.                 wp->w_flag |= flag;
  153.             wp = wp->w_wndp;
  154.         }
  155.  
  156.         /* next screen! */
  157.         scrp = scrp->s_next_screen;
  158.     }
  159. }
  160.  
  161. PASCAL NEAR insspace(f, n)    /* insert spaces forward into text */
  162.  
  163. int f, n;    /* default flag and numeric argument */
  164.  
  165. {
  166.     linsert(n, ' ');
  167.     backchar(f, n);
  168. }
  169.  
  170. /*
  171.  * linstr -- Insert a string at the current point
  172.  */
  173.  
  174. int PASCAL NEAR linstr(instr)
  175. char    *instr;
  176. {
  177.     register int status;
  178.  
  179.     status = TRUE;
  180.     if (instr != NULL)
  181.         while (*instr) {
  182.             status = ((*instr == '\r') ? lnewline(): linsert(1, *instr));
  183.  
  184.             /* Insertion error? */
  185.             if (status != TRUE) {
  186.                 mlwrite(TEXT168);
  187. /*                                      "%%Can not insert string" */
  188.                 break;
  189.             }
  190.             instr++;
  191.         }
  192.     return(status);
  193. }
  194.  
  195. /*
  196.  * Insert "n" copies of the character "c" at the current location of dot. In
  197.  * the easy case all that happens is the text is stored in the line. In the
  198.  * hard case, the line has to be reallocated. When the window list is updated,
  199.  * take special care; I screwed it up once. You always update dot in the
  200.  * current window. You update mark, and a dot in another window, if it is
  201.  * greater than the place where you did the insert. Return TRUE if all is
  202.  * well, and FALSE on errors.
  203.  */
  204.  
  205. #if    PROTO
  206. PASCAL NEAR linsert(int n, char c)
  207. #else
  208. PASCAL NEAR linsert(n, c)
  209.  
  210. int    n;
  211. char    c;
  212. #endif
  213.  
  214. {
  215.     register char    *cp1;
  216.     register char    *cp2;
  217.     register LINE    *lp1;
  218.     register LINE    *lp2;
  219.     register LINE    *lp3;
  220.     register int    doto;
  221.     register int    i;
  222.     register WINDOW *wp;
  223.     SCREEN *scrp;        /* screen to fix pointers in */
  224.     int cmark;        /* current mark */
  225.  
  226.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  227.         return(rdonly());    /* we are in read only mode    */
  228.  
  229.     /* a zero insert means do nothing! */
  230.     if (n == 0)
  231.         return(TRUE);
  232.  
  233.     /* Negative numbers of inserted characters are right out! */
  234.     if (n < 1)
  235.         return(FALSE);
  236.  
  237.     /* mark the current window's buffer as changed */
  238.     lchange(WFEDIT);
  239.  
  240.     lp1 = curwp->w_dotp;            /* Current line     */
  241.     if (lp1 == curbp->b_linep) {        /* At the end: special    */
  242.         if (curwp->w_doto != 0) {
  243.             mlwrite(TEXT170);
  244. /*                              "bug: linsert" */
  245.             return(FALSE);
  246.         }
  247.         if ((lp2=lalloc(BSIZE(n))) == NULL)    /* Allocate new line    */
  248.             return(FALSE);
  249.         lp2->l_used = n;
  250.         lp3 = lp1->l_bp;        /* Previous line    */
  251.         lp3->l_fp = lp2;        /* Link in        */
  252.         lp2->l_fp = lp1;
  253.         lp1->l_bp = lp2;
  254.         lp2->l_bp = lp3;
  255.         for (i=0; i<n; ++i)
  256.             lp2->l_text[i] = c;
  257.         curwp->w_dotp = lp2;
  258.         curwp->w_doto = n;
  259.         return(TRUE);
  260.     }
  261.     doto = curwp->w_doto;            /* Save for later.    */
  262.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  263.         if ((lp2=lalloc(BSIZE(lp1->l_used+n))) == NULL)
  264.             return(FALSE);
  265.         lp2->l_used = lp1->l_used+n;
  266.         cp1 = &lp1->l_text[0];
  267.         cp2 = &lp2->l_text[0];
  268.         while (cp1 != &lp1->l_text[doto])
  269.             *cp2++ = *cp1++;
  270.         cp2 += n;
  271.         while (cp1 != &lp1->l_text[lp1->l_used])
  272.             *cp2++ = *cp1++;
  273.         lp1->l_bp->l_fp = lp2;
  274.         lp2->l_fp = lp1->l_fp;
  275.         lp1->l_fp->l_bp = lp2;
  276.         lp2->l_bp = lp1->l_bp;
  277.         free((char *) lp1);
  278.     } else {                /* Easy: in place    */
  279.         lp2 = lp1;            /* Pretend new line    */
  280.         lp2->l_used += n;
  281.         cp2 = &lp1->l_text[lp1->l_used];
  282.         cp1 = cp2-n;
  283.         while (cp1 != &lp1->l_text[doto])
  284.             *--cp2 = *--cp1;
  285.     }
  286.     for (i=0; i<n; ++i)            /* Add the characters    */
  287.         lp2->l_text[doto+i] = c;
  288.     /* in all screens.... */
  289.     scrp = first_screen;
  290.     while (scrp) {
  291.  
  292.         wp = scrp->s_first_window;
  293.         while (wp != NULL) {
  294.             if (wp->w_linep == lp1)
  295.                 wp->w_linep = lp2;
  296.             if (wp->w_dotp == lp1) {
  297.                 wp->w_dotp = lp2;
  298.                 if (wp==curwp || wp->w_doto>doto)
  299.                     wp->w_doto += n;
  300.             }
  301.             for (cmark = 0; cmark < NMARKS; cmark++) {
  302.                 if (wp->w_markp[cmark] == lp1) {
  303.                     wp->w_markp[cmark] = lp2;
  304.                     if (wp->w_marko[cmark] > doto)
  305.                         wp->w_marko[cmark] += n;
  306.                 }
  307.             }
  308.             wp = wp->w_wndp;
  309.         }
  310.  
  311.         /* next screen! */
  312.         scrp = scrp->s_next_screen;
  313.     }
  314.     return(TRUE);
  315. }
  316.  
  317. /*
  318.  * Overwrite a character into the current line at the current position
  319.  *
  320.  */
  321.  
  322. #if    PROTO
  323. PASCAL NEAR lowrite(char c)
  324. #else
  325. PASCAL NEAR lowrite(c)
  326.  
  327. char c;        /* character to overwrite on current position */
  328. #endif
  329.  
  330. {
  331.     if (curwp->w_doto < curwp->w_dotp->l_used &&
  332.         (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
  333.          (curwp->w_doto) % 8 == 7))
  334.             ldelete(1L, FALSE);
  335.     return(linsert(1, c));
  336. }
  337.  
  338. /*
  339.  * lover -- Overwrite a string at the current point
  340.  */
  341.  
  342. int PASCAL NEAR lover(ostr)
  343.  
  344. char    *ostr;
  345.  
  346. {
  347.     register int status = TRUE;
  348.  
  349.     if (ostr != NULL)
  350.         while (*ostr && status == TRUE) {
  351.             status = ((*ostr == '\r') ? lnewline(): lowrite(*ostr));
  352.  
  353.             /* Insertion error? */
  354.             if (status != TRUE) {
  355.                 mlwrite(TEXT172);
  356. /*                                      "%%Out of memory while overwriting" */
  357.                 break;
  358.             }
  359.             ostr++;
  360.         }
  361.     return(status);
  362. }
  363.  
  364. /*
  365.  * Insert a newline into the buffer at the current location of dot in the
  366.  * current window. The funny ass-backwards way it does things is not a botch;
  367.  * it just makes the last line in the file not a special case. Return TRUE if
  368.  * everything works out and FALSE on error (memory allocation failure). The
  369.  * update of dot and mark is a bit easier then in the above case, because the
  370.  * split forces more updating.
  371.  */
  372. int PASCAL NEAR lnewline()
  373. {
  374.     register char    *cp1;
  375.     register char    *cp2;
  376.     register LINE    *lp1;
  377.     register LINE    *lp2;
  378.     register int    doto;
  379.     register WINDOW *wp;
  380.     SCREEN *scrp;        /* screen to fix pointers in */
  381.     int cmark;        /* current mark */
  382.  
  383.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  384.         return(rdonly());    /* we are in read only mode    */
  385.     lchange(WFHARD);
  386.     lp1  = curwp->w_dotp;            /* Get the address and    */
  387.     doto = curwp->w_doto;            /* offset of "."    */
  388.     if ((lp2=lalloc(doto)) == NULL)     /* New first half line    */
  389.         return(FALSE);
  390.     cp1 = &lp1->l_text[0];            /* Shuffle text around    */
  391.     cp2 = &lp2->l_text[0];
  392.     while (cp1 != &lp1->l_text[doto])
  393.         *cp2++ = *cp1++;
  394.     cp2 = &lp1->l_text[0];
  395.     while (cp1 != &lp1->l_text[lp1->l_used])
  396.         *cp2++ = *cp1++;
  397.     lp1->l_used -= doto;
  398.     lp2->l_bp = lp1->l_bp;
  399.     lp1->l_bp = lp2;
  400.     lp2->l_bp->l_fp = lp2;
  401.     lp2->l_fp = lp1;
  402.  
  403.     /* in all screens.... */
  404.     scrp = first_screen;
  405.     while (scrp) {
  406.  
  407.         wp = scrp->s_first_window;
  408.         while (wp != NULL) {
  409.             if (wp->w_linep == lp1)
  410.                 wp->w_linep = lp2;
  411.             if (wp->w_dotp == lp1) {
  412.                 if (wp->w_doto < doto)
  413.                     wp->w_dotp = lp2;
  414.                 else
  415.                     wp->w_doto -= doto;
  416.             }
  417.             for (cmark = 0; cmark < NMARKS; cmark++) {
  418.                 if (wp->w_markp[cmark] == lp1) {
  419.                     if (wp->w_marko[cmark] < doto)
  420.                         wp->w_markp[cmark] = lp2;
  421.                     else
  422.                         wp->w_marko[cmark] -= doto;
  423.                 }
  424.             }
  425.             wp = wp->w_wndp;
  426.         }
  427.  
  428.         /* next screen! */
  429.         scrp = scrp->s_next_screen;
  430.     }
  431.     return(TRUE);
  432. }
  433.  
  434. /*
  435.  
  436. LDELETE:
  437.  
  438.     This function deletes "n" bytes, starting at dot. Positive n
  439. deletes forward, negative n deletes backwords. It understands how to
  440. deal with end of lines, and with two byte characters. It returns TRUE
  441. if all of the characters were deleted, and FALSE if they were not
  442. (because dot ran into the buffer end). The "kflag" is TRUE if the text
  443. should be put in the kill buffer.
  444.  
  445. */
  446.  
  447. PASCAL NEAR ldelete(n, kflag)
  448.  
  449. long n;     /* # of chars to delete */
  450. int kflag;    /* put killed text in kill buffer flag */
  451.  
  452. {
  453.     register char    *cp1;
  454.     register char    *cp2;
  455.     register LINE    *dotp;
  456.     register int    doto;
  457.     register int    chunk;
  458.     register WINDOW *wp;
  459.     int cmark;        /* current mark */
  460.  
  461.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  462.         return(rdonly());    /* we are in read only mode    */
  463.  
  464.     /* going Forward? */
  465.     if (n >= 0) {
  466.  
  467.         while (n > 0) {
  468. #if    DBCS
  469.             /* never start forward on a 2 byte char */
  470.             if (curwp->w_doto > 0 && is2byte(curwp->w_dotp->l_text,
  471.                 &curwp->w_dotp->l_text[curwp->w_doto - 1])) {
  472.                 curwp->w_doto--;
  473.                 n++;
  474.             }
  475. #endif
  476.             /* record the current point */
  477.             dotp = curwp->w_dotp;
  478.             doto = curwp->w_doto;
  479.     
  480.             /* can't delete past the end of the buffer */
  481.             if (dotp == curbp->b_linep)
  482.                 return(FALSE);
  483.     
  484.             /* find out how many chars to delete on this line */
  485.             chunk = dotp->l_used-doto;    /* Size of chunk.    */
  486.             if (chunk > n)
  487.                 chunk = n;
  488.     
  489.             /* if at the end of a line, merge with the next */
  490.             if (chunk == 0) {
  491.     
  492.                 /* flag that we are making a hard change */
  493.                 lchange(WFHARD);
  494.                 if (ldelnewline() == FALSE ||
  495.                     (kflag != FALSE &&
  496.                      kinsert(FORWARD, '\r')==FALSE))
  497.                     return(FALSE);
  498.                 --n;
  499.                 continue;
  500.             }
  501.     
  502.             /* flag the fact we are changing the current line */
  503.             lchange(WFEDIT);
  504.     
  505.             /* find the limits of the kill */
  506.             cp1 = &dotp->l_text[doto];
  507.             cp2 = cp1 + chunk;
  508. #if    DBCS
  509.             /* never leave half a character */
  510.             if (is2byte(dotp->l_text, cp2 - 1)) {
  511.                 ++chunk;
  512.                 ++cp2;
  513.                 ++n;
  514.             }
  515. #endif
  516.  
  517.             /* save the text to the kill buffer */
  518.             if (kflag != FALSE) {
  519.                 while (cp1 != cp2) {
  520.                     if (kinsert(FORWARD, *cp1) == FALSE)
  521.                         return(FALSE);
  522.                     ++cp1;
  523.                 }
  524.                 cp1 = &dotp->l_text[doto];
  525.             }
  526.     
  527.             /* copy what is left of the line upward */
  528.             while (cp2 != &dotp->l_text[dotp->l_used])
  529.                 *cp1++ = *cp2++;
  530.             dotp->l_used -= chunk;
  531.     
  532.             /* fix any other windows with the same text displayed */
  533.             wp = wheadp;
  534.             while (wp != NULL) {
  535.     
  536.                 /* reset the dot if needed */
  537.                 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  538.                     wp->w_doto -= chunk;
  539.                     if (wp->w_doto < doto)
  540.                         wp->w_doto = doto;
  541.                 }
  542.     
  543.                 /* reset any marks if needed */
  544.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  545.                     if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
  546.                         wp->w_marko[cmark] -= chunk;
  547.                         if (wp->w_marko[cmark] < doto)
  548.                             wp->w_marko[cmark] = doto;
  549.                     }
  550.                 }
  551.     
  552.                 /* onward to the next window */
  553.                 wp = wp->w_wndp;
  554.             }
  555.     
  556.             /* indicate we have deleted chunk characters */
  557.             n -= chunk;
  558.         }
  559.     } else {
  560.         while (n < 0) {
  561. #if    DBCS
  562.             /* never start backwards on the
  563.                1st of a 2 byte character */
  564.             if (curwp->w_doto > 1 && is2byte(curwp->w_dotp->l_text,
  565.                 &curwp->w_dotp->l_text[curwp->w_doto-1])) {
  566.                 curwp->w_doto++;
  567.                 n--;
  568.             }
  569. #endif
  570.             /* record the current point */
  571.             dotp = curwp->w_dotp;
  572.             doto = curwp->w_doto;
  573.     
  574.             /* can't delete past the beginning of the buffer */
  575.             if (dotp == lforw(curbp->b_linep) && (doto == 0))
  576.                 return(FALSE);
  577.     
  578.             /* find out how many chars to delete on this line */
  579.             chunk = doto;        /* Size of chunk.    */
  580.             if (chunk > -n)
  581.                 chunk = -n;
  582.     
  583.             /* if at the beginning of a line, merge with the last */
  584.             if (chunk == 0) {
  585.     
  586.                 /* flag that we are making a hard change */
  587.                 lchange(WFHARD);
  588.                 backchar(TRUE, 1);
  589.                 if (ldelnewline() == FALSE ||
  590.                     (kflag != FALSE &&
  591.                      kinsert(BACKWARD, '\r')==FALSE))
  592.                     return(FALSE);
  593.                 ++n;
  594.                 continue;
  595.             }
  596.     
  597.             /* flag the fact we are changing the current line */
  598.             lchange(WFEDIT);
  599.     
  600.             /* find the limits of the kill */
  601.             cp1 = &dotp->l_text[doto];
  602.             cp2 = cp1 - chunk;
  603. #if    DBCS
  604.             if (is2byte(dotp->l_text, cp2 - 1)) {
  605.                 ++chunk;
  606.                 --cp2;
  607.                 ++n;
  608.             }
  609. #endif
  610.     
  611.             /* save the text to the kill buffer */
  612.             if (kflag != FALSE) {
  613.                 while (cp1 > cp2) {
  614.                     if (kinsert(BACKWARD, *(--cp1)) == FALSE)
  615.                         return(FALSE);
  616.                 }
  617.                 cp1 = &dotp->l_text[doto];
  618.             }
  619.     
  620.             /* copy what is left of the line downward */
  621.             while (cp1 != &dotp->l_text[dotp->l_used])
  622.                 *cp2++ = *cp1++;
  623.             dotp->l_used -= chunk;
  624.             curwp->w_doto -= chunk;
  625.     
  626.             /* fix any other windows with the same text displayed */
  627.             wp = wheadp;
  628.             while (wp != NULL) {
  629.     
  630.                 /* reset the dot if needed */
  631.                 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  632.                     wp->w_doto -= chunk;
  633.                     if (wp->w_doto < doto)
  634.                         wp->w_doto = doto;
  635.                 }
  636.     
  637.                 /* reset any marks if needed */
  638.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  639.                     if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
  640.                         wp->w_marko[cmark] -= chunk;
  641.                         if (wp->w_marko[cmark] < doto)
  642.                             wp->w_marko[cmark] = doto;
  643.                     }
  644.                 }
  645.     
  646.                 /* onward to the next window */
  647.                 wp = wp->w_wndp;
  648.             }
  649.     
  650.             /* indicate we have deleted chunk characters */
  651.             n += chunk;
  652.         }
  653.     }
  654.     return(TRUE);
  655. }
  656.  
  657. /* getctext:    grab and return a string with the text of
  658.         the current line
  659. */
  660.  
  661. char *PASCAL NEAR getctext()
  662.  
  663. {
  664.     register LINE *lp;    /* line to copy */
  665.     register int size;    /* length of line to return */
  666.     register char *sp;    /* string pointer into line */
  667.     register char *dp;    /* string pointer into returned line */
  668.     char rline[NSTRING];    /* line to return */
  669.  
  670.     /* find the contents of the current line and its length */
  671.     lp = curwp->w_dotp;
  672.     sp = lp->l_text;
  673.     size = lp->l_used;
  674.     if (size >= NSTRING)
  675.         size = NSTRING - 1;
  676.  
  677.     /* copy it across */
  678.     dp = rline;
  679.     while (size--)
  680.         *dp++ = *sp++;
  681.     *dp = 0;
  682.     return(rline);
  683. }
  684.  
  685. /* putctext:    replace the current line with the passed in text    */
  686.  
  687. PASCAL NEAR putctext(iline)
  688.  
  689. char *iline;    /* contents of new line */
  690.  
  691. {
  692.     register int status;
  693.  
  694.     /* delete the current line */
  695.     curwp->w_doto = 0;    /* starting at the beginning of the line */
  696.     if ((status = killtext(TRUE, 1)) != TRUE)
  697.         return(status);
  698.  
  699.     /* insert the new line */
  700.     if ((status = linstr(iline)) != TRUE)
  701.         return(status);
  702.     status = lnewline();
  703.     backline(TRUE, 1);
  704.     return(status);
  705. }
  706.  
  707. /*
  708.  * Delete a newline. Join the current line with the next line. If the next line
  709.  * is the magic header line always return TRUE; merging the last line with the
  710.  * header line can be thought of as always being a successful operation, even
  711.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  712.  * can be done by shuffling data around. Hard cases require that lines be moved
  713.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  714.  * "ldelete" only.
  715.  */
  716. int PASCAL NEAR ldelnewline()
  717. {
  718.     register char    *cp1;
  719.     register char    *cp2;
  720.     register LINE    *lp1;
  721.     register LINE    *lp2;
  722.     register LINE    *lp3;
  723.     register WINDOW *wp;
  724.     SCREEN *scrp;        /* screen to fix pointers in */
  725.     int cmark;        /* current mark */
  726.  
  727.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  728.         return(rdonly());    /* we are in read only mode    */
  729.     lp1 = curwp->w_dotp;
  730.     lp2 = lp1->l_fp;
  731.     if (lp2 == curbp->b_linep) {        /* At the buffer end.    */
  732.         if (lp1->l_used == 0)        /* Blank line.        */
  733.             lfree(lp1);
  734.         return(TRUE);
  735.     }
  736.     if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  737.         cp1 = &lp1->l_text[lp1->l_used];
  738.         cp2 = &lp2->l_text[0];
  739.         while (cp2 != &lp2->l_text[lp2->l_used])
  740.         *cp1++ = *cp2++;
  741.  
  742.         /* in all screens.... */
  743.         scrp = first_screen;
  744.         while (scrp) {
  745.  
  746.             wp = scrp->s_first_window;
  747.             while (wp != NULL) {
  748.                 if (wp->w_linep == lp2)
  749.                     wp->w_linep = lp1;
  750.                 if (wp->w_dotp == lp2) {
  751.                     wp->w_dotp  = lp1;
  752.                     wp->w_doto += lp1->l_used;
  753.                 }
  754.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  755.                     if (wp->w_markp[cmark] == lp2) {
  756.                         wp->w_markp[cmark]  = lp1;
  757.                         wp->w_marko[cmark] += lp1->l_used;
  758.                     }
  759.                 }
  760.                 wp = wp->w_wndp;
  761.             }
  762.  
  763.             /* next screen! */
  764.             scrp = scrp->s_next_screen;
  765.         }
  766.  
  767.         lp1->l_used += lp2->l_used;
  768.         lp1->l_fp = lp2->l_fp;
  769.         lp2->l_fp->l_bp = lp1;
  770.         free((char *) lp2);
  771.         return(TRUE);
  772.     }
  773.     if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  774.         return(FALSE);
  775.     cp1 = &lp1->l_text[0];
  776.     cp2 = &lp3->l_text[0];
  777.     while (cp1 != &lp1->l_text[lp1->l_used])
  778.         *cp2++ = *cp1++;
  779.     cp1 = &lp2->l_text[0];
  780.     while (cp1 != &lp2->l_text[lp2->l_used])
  781.         *cp2++ = *cp1++;
  782.     lp1->l_bp->l_fp = lp3;
  783.     lp3->l_fp = lp2->l_fp;
  784.     lp2->l_fp->l_bp = lp3;
  785.     lp3->l_bp = lp1->l_bp;
  786.  
  787.     /* in all screens.... */
  788.     scrp = first_screen;
  789.     while (scrp) {
  790.  
  791.         wp = scrp->s_first_window;
  792.         while (wp != NULL) {
  793.             if (wp->w_linep==lp1 || wp->w_linep==lp2)
  794.                 wp->w_linep = lp3;
  795.             if (wp->w_dotp == lp1)
  796.                 wp->w_dotp  = lp3;
  797.             else if (wp->w_dotp == lp2) {
  798.                 wp->w_dotp  = lp3;
  799.                 wp->w_doto += lp1->l_used;
  800.             }
  801.             for (cmark = 0; cmark < NMARKS; cmark++) {
  802.                 if (wp->w_markp[cmark] == lp1)
  803.                     wp->w_markp[cmark]  = lp3;
  804.                 else if (wp->w_markp[cmark] == lp2) {
  805.                     wp->w_markp[cmark]  = lp3;
  806.                     wp->w_marko[cmark] += lp1->l_used;
  807.                 }
  808.             }
  809.             wp = wp->w_wndp;
  810.         }
  811.  
  812.         /* next screen! */
  813.         scrp = scrp->s_next_screen;
  814.     }
  815.  
  816.     free((char *) lp1);
  817.     free((char *) lp2);
  818.     return(TRUE);
  819. }
  820.  
  821. /*    Add a new line to the end of the indicated buffer.
  822.     return FALSE if we run out of memory
  823.     note that this works on non-displayed buffers as well!
  824. */
  825.  
  826. #if    PROTO
  827. int PASCAL NEAR addline(BUFFER *bp, char *text)
  828. #else
  829. int PASCAL NEAR addline(bp, text)
  830.  
  831. BUFFER *bp;    /* buffer to add text to */
  832. char *text;    /* line to add */
  833. #endif
  834. {
  835.     register LINE    *lp;
  836.     register int    i;
  837.     register int    ntext;
  838.  
  839.     /* allocate the memory to hold the line */
  840.     ntext = strlen(text);
  841.     if ((lp=lalloc(ntext)) == NULL)
  842.         return(FALSE);
  843.  
  844.     /* copy the text into the new line */
  845.     for (i=0; i<ntext; ++i)
  846.         lputc(lp, i, text[i]);
  847.  
  848.     /* add the new line to the end of the buffer */
  849.     bp->b_linep->l_bp->l_fp = lp;
  850.     lp->l_bp = bp->b_linep->l_bp;
  851.     bp->b_linep->l_bp = lp;
  852.     lp->l_fp = bp->b_linep;
  853.  
  854.     /* if the point was at the end of the buffer,
  855.        move it to the beginning of the new line */
  856.     if (bp->b_dotp == bp->b_linep)
  857.         bp->b_dotp = lp;
  858.     return(TRUE);
  859. }
  860.  
  861. /*
  862.  * Delete all of the text saved in the kill buffer. Called by commands when a
  863.  * new kill context is being created. The kill buffer array is released, just
  864.  * in case the buffer has grown to immense size. No errors.
  865.  */
  866.  
  867. void kdelete()
  868.  
  869. {
  870.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  871.  
  872.     if (kbufh[kill_index] != NULL) {
  873.  
  874.         /* first, delete all the chunks */
  875.         kbufp[kill_index] = kbufh[kill_index];
  876.         while (kbufp[kill_index] != NULL) {
  877.             kp = kbufp[kill_index]->d_next;
  878.             free((char *)kbufp[kill_index]);
  879.             kbufp[kill_index] = kp;
  880. #if     WINDOW_MSWIN
  881.             {
  882.                 static int  o = 0;
  883.                 if (--o < 0) {
  884.                 longop(TRUE);
  885.                 o = 10;     /* to lower overhead, only
  886.                            10% calls to longop */
  887.                 }
  888.             }
  889. #endif
  890.         }
  891.  
  892.         /* and reset all the kill buffer pointers */
  893.         kbufh[kill_index] = kbufp[kill_index] = NULL;
  894.         kskip[kill_index] = 0;
  895.         kused[kill_index] = KBLOCK;             
  896.     }
  897. }
  898.  
  899. /*    next_kill:    advance to the next position in the kill ring,
  900.             pushing the current kill buffer and clearing
  901.             what will be the new kill buffer
  902. */
  903.  
  904. PASCAL NEAR next_kill()
  905.  
  906. {
  907.     /* advance to the next kill ring entry */
  908.     kill_index++;
  909.     if (kill_index == NRING)
  910.         kill_index = 0;
  911.  
  912.     /* and clear it, so it is ready for use */
  913.     kdelete();
  914. }
  915.  
  916. /*
  917.  * Insert a character to the kill buffer, allocating new chunks as needed.
  918.  * Return TRUE if all is well, and FALSE on errors.
  919.  */
  920.  
  921. #if    PROTO
  922. PASCAL NEAR kinsert(int direct, char c)
  923. #else
  924. PASCAL NEAR kinsert(direct, c)
  925.  
  926. int direct;    /* direction (FORWARD/BACKWARD) to insert characters */
  927. char c;        /* character to insert in the kill buffer */
  928. #endif
  929.  
  930. {
  931.     KILL *nchunk;    /* ptr to newly malloced chunk */
  932.  
  933.     if (direct == FORWARD) {
  934.  
  935.         /* check to see if we need a new chunk */
  936.         if (kused[kill_index] >= KBLOCK) {
  937.             if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  938.                 return(FALSE);
  939.             if (kbufh[kill_index] == NULL)    /* set head ptr if first time */
  940.                 kbufh[kill_index] = nchunk;
  941.             if (kbufp[kill_index] != NULL)    /* point the current to this new one */
  942.                 kbufp[kill_index]->d_next = nchunk;
  943.             kbufp[kill_index] = nchunk;
  944.             kbufp[kill_index]->d_next = NULL;
  945.             kused[kill_index] = 0;
  946. #if     WINDOW_MSWIN
  947.             {
  948.                 static int  o = 0;
  949.                 if (--o < 0) {
  950.                 longop(TRUE);
  951.                 o = 10;      /* to lower overhead, only
  952.                            10% calls to longop */
  953.                 }
  954.             }
  955. #endif
  956.         }
  957.     
  958.         /* and now insert the character */
  959.         kbufp[kill_index]->d_chunk[kused[kill_index]++] = c;
  960.     } else {
  961.         /* BACKWARDS */
  962.         /* check to see if we need a new chunk */
  963.         if (kskip[kill_index] == 0) {
  964.             if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  965.                 return(FALSE);
  966.             if (kbufh[kill_index] == NULL) {    /* set head ptr if first time */
  967.                 kbufh[kill_index] = nchunk;
  968.                 kbufp[kill_index] = nchunk;
  969.                 kskip[kill_index] = KBLOCK;
  970.                 kused[kill_index] = KBLOCK;
  971.                 nchunk->d_next = (KILL *)NULL;
  972.             } else {
  973.                 nchunk->d_next = kbufh[kill_index];
  974.                 kbufh[kill_index] = nchunk;
  975.                 kskip[kill_index] = KBLOCK;
  976.             }
  977. #if     WINDOW_MSWIN
  978.             {
  979.                 static int  o = 0;
  980.                 if (--o < 0) {
  981.                 longop(TRUE);
  982.                 o = 10;      /* to lower overhead, only
  983.                            10% calls to longop */
  984.                 }
  985.             }
  986. #endif
  987.         }
  988.     
  989.         /* and now insert the character */
  990.         kbufh[kill_index]->d_chunk[--kskip[kill_index]] = c;
  991.     }
  992.     return(TRUE);
  993. }
  994.  
  995. /*
  996.  * Yank text back from the kill buffer. This is really easy. All of the work
  997.  * is done by the standard insert routines. All you do is run the loop, and
  998.  * check for errors. Bound to "C-Y".
  999.  */
  1000.  
  1001. #define    Char_insert(a)    (a == '\r' ? lnewline() : linsert(1, a))
  1002.  
  1003. PASCAL NEAR yank(f, n)
  1004.  
  1005. int f,n;    /* prefix flag and argument */
  1006.  
  1007. {
  1008.     register int counter;    /* counter into kill buffer data */
  1009.     register char *sp;    /* pointer into string to insert */
  1010.     short int curoff;    /* storage for line before yanking */
  1011.     LINE *curline;
  1012.     KILL *kptr;        /* pointer into kill buffer */
  1013.  
  1014.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  1015.         return(rdonly());    /* we are in read only mode    */
  1016.     if (n < 0)
  1017.         return(FALSE);
  1018.  
  1019.     /* make sure there is something to yank */
  1020.     if (kbufh[kill_index] == NULL) {
  1021.         last_size = 0L;
  1022.         return(TRUE);        /* not an error, just nothing */
  1023.     }
  1024.  
  1025.     /*
  1026.      * Save the local pointers to hold global ".".
  1027.      */
  1028.     if (yankflag) {
  1029.         /* Find the *previous* line, since the line we are on
  1030.          * may disappear due to re-allocation.  This works even
  1031.          * if we are on the first line of the file.
  1032.          */
  1033.         curline = lback(curwp->w_dotp);
  1034.         curoff = curwp->w_doto;
  1035.     }
  1036.  
  1037.     /* for each time.... */
  1038.     while (n--) {
  1039.         last_size = 0L;
  1040.         if (kskip[kill_index] > 0) {
  1041.             kptr = kbufh[kill_index];
  1042.             sp = &(kptr->d_chunk[kskip[kill_index]]);
  1043.             counter = kskip[kill_index];
  1044.             while (counter++ < KBLOCK) {
  1045.                 Char_insert(*sp);
  1046.                 last_size++;
  1047.                 ++sp;
  1048.             }
  1049.             kptr = kptr->d_next;
  1050.         } else {
  1051.             kptr = kbufh[kill_index];
  1052.         }
  1053.     
  1054.         if (kptr != (KILL *)NULL) {
  1055.             while (kptr != kbufp[kill_index]) {
  1056.                 sp = kptr->d_chunk;
  1057.                 for(counter = 0; counter < KBLOCK; counter++) {
  1058.                     Char_insert(*sp);
  1059.                     last_size++;
  1060.                     ++sp;
  1061.                 }
  1062.                 kptr = kptr->d_next;
  1063.             }
  1064.             counter = kused[kill_index];
  1065.             sp = kptr->d_chunk;
  1066.             while (counter--) {
  1067.                 Char_insert(*sp);
  1068.                 last_size++;
  1069.                 ++sp;
  1070.             }
  1071.         }
  1072.     }
  1073.  
  1074.     /* If requested, set global "." back to the
  1075.      * beginning of the yanked text.
  1076.      */
  1077.     if (yankflag) {
  1078.         curwp->w_dotp = lforw(curline);
  1079.         curwp->w_doto = curoff;
  1080.     }
  1081.     thisflag |= CFYANK;
  1082.     return(TRUE);
  1083. }
  1084.  
  1085. PASCAL NEAR cycle_ring(f, n)
  1086.  
  1087. int f,n;    /* prefix flag and argument */
  1088.  
  1089. {
  1090.     register int orig_index;    /* original kill_index */
  1091.  
  1092.     /* if there is an argument, cycle the kill index */
  1093.     if (f) {
  1094.         while (n) {
  1095.             orig_index = kill_index;
  1096.             do {
  1097.                 kill_index--;
  1098.                 if (kill_index < 0)
  1099.                     kill_index = NRING - 1;
  1100.             } while ((orig_index != kill_index) &&
  1101.                 (kbufh[kill_index] == (KILL *)NULL));
  1102.             n--;
  1103.         }
  1104.     }
  1105. }
  1106.  
  1107. PASCAL NEAR yank_pop(f, n)
  1108.  
  1109. int f,n;    /* prefix flag and argument */
  1110.  
  1111. {
  1112.     /* defaulted non first call will cycle by 1 */
  1113.     if ((lastflag & CFYANK) && (f == FALSE)) {
  1114.         f = TRUE;
  1115.         n = 1;
  1116.     }
  1117.  
  1118.     /* cycle the kill ring appropriately */
  1119.     cycle_ring(f, n);
  1120.  
  1121.     /* if not the first consectutive time, delete the last yank */
  1122.     if ((lastflag & CFYANK))
  1123.         ldelete(-last_size, FALSE);
  1124.  
  1125.     /* and insert the current kill buffer */
  1126.     return(yank(FALSE, 1));
  1127. }
  1128.  
  1129. PASCAL NEAR clear_ring(f, n)
  1130.  
  1131. int f,n;    /* prefix flag and argument */
  1132.  
  1133. {
  1134.     register int index;
  1135.  
  1136.     for (index = 0; index < NRING; index++)
  1137.         next_kill();
  1138.     mlwrite(TEXT228);
  1139. /*        "[Kill ring cleared]" */
  1140.     return(TRUE);
  1141. }
  1142.  
  1143. #if    0
  1144. dispkill()
  1145.  
  1146. {
  1147.     KILL *kptr;
  1148.     int index;
  1149.     char *sp;
  1150.     int counter;
  1151.  
  1152.     if (kbufh[kill_index] == (KILL *)NULL) {
  1153.         printf("<EMPTY>\n");
  1154.         return;
  1155.     }
  1156.  
  1157.     index = 1;
  1158.     if (kskip[kill_index] > 0) {
  1159.         kptr = kbufh[kill_index];
  1160.         printf("kskip[kill_index] = %d\nBLOCK %d <", kskip[kill_index], index++);
  1161.         sp = &(kptr->d_chunk[kskip[kill_index]]);
  1162.         counter = kskip[kill_index];
  1163.         while (counter++ < KBLOCK) {
  1164.             putchar(*sp++);
  1165.         }
  1166.         printf(">\n");
  1167.         kptr = kptr->d_next;
  1168.     } else {
  1169.         kptr = kbufh[kill_index];
  1170.     }
  1171.  
  1172.     if (kptr != (KILL *)NULL) {
  1173.         while (kptr != kbufp[kill_index]) {
  1174.             printf("BLOCK %d <%255s>\n", index++, kptr->d_chunk);
  1175.             kptr = kptr->d_next;
  1176.         }
  1177.         printf("BLOCK %d <", index++);
  1178.         counter = kused[kill_index];
  1179.         sp = kptr->d_chunk;
  1180.         while (counter--) {
  1181.             putchar(*sp++);
  1182.         }
  1183.         printf(">\nkused[kill_index] = %d\n", kused[kill_index]);
  1184.     }
  1185.  
  1186. }
  1187. #endif
  1188.